/**
 * 
 */
package de.cbf.cam.dsa.model.entities.items.transfers;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import de.cbf.cam.dsa.model.entities.SF;
import de.cbf.cam.dsa.model.entities.WeaponStyle;
import de.cbf.cam.dsa.model.entities.maneuvers.ManeuverDesc;
import de.cbf.cam.dsa.model.entities.states.OneState;
import de.cbf.cam.dsa.model.entities.states.StateBloodLoss;
import de.cbf.cam.dsa.model.entities.states.StateNoCloseCombat;
import de.cbf.cam.dsa.model.entities.states.StateOnGround;
import de.cbf.cam.dsa.model.entities.states.StateUnconscious;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerATPA;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerAttribute;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerBase;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerEnergy;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerINI;
import de.cbf.cam.dsa.model.entities.states.stateChanger.StateChangerState;
import de.cbf.cam.dsa.model.entities.weapon.valueModifier.WeaponINIModifier;
import de.cbf.cam.dsa.model.entities.weapon.valueModifier.WeaponTPKKModifier;
import de.cbf.cam.dsa.model.entities.weapon.valueModifier.WeaponTPModifier;
import de.cbf.cam.dsa.model.entities.weapon.valueModifier.WeaponValueModifier;
import de.cbf.cam.dsa.model.entities.weapon.valueModifier.WeaponWMModifier;
import de.cbf.cam.dsa.model.prerequisites.AttributePrereq;
import de.cbf.cam.dsa.model.prerequisites.Prereq;
import de.cbf.cam.dsa.model.prerequisites.SfPrereq;

/**
 * @author Nebelritter
 *
 */
public class TransferSubEntities {
	
	static void writeString(String text,DataOutputStream target) throws IOException{
		byte[] buffer = text.getBytes();
   	  	target.writeInt(buffer.length);
   	  	target.write(buffer);
	}
	
	static String readString(DataInputStream source) throws IOException{
		int size = source.readInt();
		byte[] buffer = new byte[size];
		source.read(buffer);
		return new String(buffer);
	}
	
//	private static final int STATECHANGER_Base		 	= 1<<1;
	private static final int STATECHANGER_Attribute 	= 1<<2;
	private static final int STATECHANGER_ATPA			= 1<<3;
	private static final int STATECHANGER_ENERGY 		= 1<<4;
	private static final int STATECHANGER_INI  			= 1<<5;
	private static final int STATECHANGER_State 		= 1<<6;	
	
	static void writeStateChanger(StateChangerBase stateChanger,DataOutputStream writeOut) throws IOException{
		//write base data
		writeString(stateChanger.getGuid(), writeOut);
		writeOut.writeInt(stateChanger.getBaseRoundsTillEffective());
		writeOut.writeInt(stateChanger.getLeftRoundsTillEffective());
		writeOut.writeBoolean(stateChanger.isRepeating());
		//check for exact stateChangerType and write with stateChangerType identifier
		if (stateChanger instanceof StateChangerAttribute) {
			StateChangerAttribute stateChangerAttribute = (StateChangerAttribute) stateChanger;			
			writeOut.writeInt(STATECHANGER_Attribute);
			writeString(stateChangerAttribute.getAttributeName(), writeOut);
			writeOut.writeInt(stateChangerAttribute.getValue());
			writeOut.writeBoolean(stateChangerAttribute.isRecalcDependantValues());
			return;
		}
		if (stateChanger instanceof StateChangerATPA) {
			StateChangerATPA stateChangeATPA = (StateChangerATPA) stateChanger;			
			writeOut.writeInt(STATECHANGER_ATPA);
			String hand = stateChangeATPA.getHand();
			writeString(hand, writeOut);
			writeOut.writeInt(stateChangeATPA.getAmmountAT());
			writeOut.writeInt(stateChangeATPA.getAmmountPA());
			return;
		}
		if (stateChanger instanceof StateChangerEnergy) {
			StateChangerEnergy stateChangerEnergy = (StateChangerEnergy) stateChanger;	
			writeOut.writeInt(STATECHANGER_ENERGY);
			writeOut.writeInt(stateChangerEnergy.getValue());
			writeString(stateChangerEnergy.getType(), writeOut);
			return;
		}
		if (stateChanger instanceof StateChangerINI) {
			StateChangerINI stateChangerIni = (StateChangerINI) stateChanger;
			writeOut.writeInt(STATECHANGER_INI);
			writeOut.writeInt(stateChangerIni.getValue());
			return;
		}
		if (stateChanger instanceof StateChangerState) {
			StateChangerState stateChangerState = (StateChangerState) stateChanger;
			writeOut.writeInt(STATECHANGER_State);
			int countOfStates = stateChangerState.getStates().size();
			writeOut.writeInt(countOfStates);
			for(int i = 0;i<countOfStates;i++){
				OneState state = stateChangerState.getStates().get(i);
				writeState(state, writeOut);
			}
			return;
		}
		
	}
	 
	static StateChangerBase readStateChanger (DataInputStream input) throws IOException{
		//read base data
		String guid = readString(input);
		int baseRounds = input.readInt();
		int leftRounds = input.readInt();
		boolean repeating = input.readBoolean();
		//read weaponType
		int type = input.readInt();
		//
		switch (type) {
		//read further by weaponType
			case STATECHANGER_Attribute:{
				String attributeName			= readString(input);
				int value 						= input.readInt();
				boolean recalcDependantValues 	= input.readBoolean();
				StateChangerAttribute stateChanger = new StateChangerAttribute(attributeName, value, recalcDependantValues);
				setStateChangerBaseData(stateChanger, guid, baseRounds, leftRounds, repeating);		
				return stateChanger;				
			}
			case STATECHANGER_ATPA:{
				String hand 	= readString(input);
				int ammountAT 	= input.readInt();
				int ammountPA	= input.readInt();				
				StateChangerATPA stateChanger = new StateChangerATPA(ammountAT, ammountPA,hand);
				setStateChangerBaseData(stateChanger, guid, baseRounds, leftRounds, repeating);
				return stateChanger;
			}
			case STATECHANGER_ENERGY:{
				int value = input.readInt();
				String energyType = readString(input);
				StateChangerEnergy stateChanger = new StateChangerEnergy(energyType, value);
				setStateChangerBaseData(stateChanger, guid, baseRounds, leftRounds, repeating);
				return stateChanger;
			}
			case STATECHANGER_INI:{
				int value = input.readInt();
				StateChangerINI stateChanger = new StateChangerINI(value);
				setStateChangerBaseData(stateChanger, guid, baseRounds, leftRounds, repeating);
				return stateChanger;
			}
			case STATECHANGER_State:{
				int countOfStates = input.readInt();
				List<OneState> states = new ArrayList<OneState>();
				for(int i = 0;i<countOfStates;i++){
					OneState state = readState(input);
					states.add(state);					
				}
				StateChangerState stateChanger = new StateChangerState(states);
				setStateChangerBaseData(stateChanger, guid, baseRounds, leftRounds, repeating);
				return stateChanger;
			}			
		}
		return null;
	}
	
	private static void setStateChangerBaseData(StateChangerBase stateChanger,String guid,int baseRounds,int leftRounds, boolean repeating){
		stateChanger.setGuid(guid);
		stateChanger.setBaseRoundsTillEffective(baseRounds);
		stateChanger.setLeftRoundsTillEffective(leftRounds);
		stateChanger.setRepeating(repeating);		
	}
//	private static final int STATE_Base		 		= 1<<1;
	private static final int STATE_BloodLoss 		= 1<<2;
	private static final int STATE_NoCloseCombat	= 1<<3;
	private static final int STATE_OnGround 		= 1<<4;
	private static final int STATE_UNconscious  	= 1<<5;
	
	
	static void writeState(OneState state,DataOutputStream writeOut) throws IOException{
		//write basedata
		writeString(state.getGuid(),writeOut);
		writeString(state.getDescription(),writeOut);
		writeOut.writeInt(state.getStateType());
		//check for subtypes and write typeID
		if (state instanceof StateBloodLoss) {
			StateBloodLoss bloodLoss = (StateBloodLoss) state;
			writeOut.writeInt(STATE_BloodLoss);
			writeOut.writeInt(bloodLoss.getAmmount());		
			return;
		}
		if (state instanceof StateNoCloseCombat) {
			StateNoCloseCombat noCloseCombat = (StateNoCloseCombat) state;
			writeOut.writeInt(STATE_NoCloseCombat);
			writeOut.writeInt(noCloseCombat.getDuration());	
			return;
		}
		if (state instanceof StateOnGround) {
//			StateOnGround noCloseCombat = (StateOnGround) state; NO data thats why do not use it
			writeOut.writeInt(STATE_OnGround);				
			return;
		}
		if (state instanceof StateUnconscious) {
			StateUnconscious unconscious = (StateUnconscious) state;
			writeOut.writeInt(STATE_NoCloseCombat);
			writeOut.writeInt(unconscious.getDuration());	
			return;
		}
	}
	
	static OneState readState(DataInputStream input) throws IOException{
		//read baseData
		String guid = readString(input);
		String desc = readString(input);
		int type = input.readInt();
		int stateType = input.readInt();
		switch (stateType) {
			case STATE_BloodLoss:{
				int ammount = input.readInt();
				StateBloodLoss state = new StateBloodLoss(ammount);
				setStateBaseData(state, guid, desc, type);
			}
			case STATE_NoCloseCombat:{
				int duration = input.readInt();
				StateNoCloseCombat state = new StateNoCloseCombat(duration);
				setStateBaseData(state, guid, desc, type);
			}
			case STATE_OnGround:{
				StateOnGround state = new StateOnGround();
				setStateBaseData(state, guid, desc, type);
			}
			case STATE_UNconscious:{
				int duration = input.readInt();
				StateUnconscious state = new StateUnconscious(duration);
				setStateBaseData(state, guid, desc, type);
			}
		
		}
		return null;
	}
	
	private static void setStateBaseData(OneState state,String guid,String description,int type){
		state.setGuid(guid);
		state.setStateType(type);
		state.setDescription(description);
	}
	
//	private static final int PREREQ_Base	 		= 1<<1;
	private static final int PREREQ_Attribute 		= 1<<2;
	private static final int PREREQ_SF		 		= 1<<3;
	
	static void writePrereq(Prereq prereq,DataOutputStream writeOut) throws IOException{
		//write basedata
		writeString(prereq.getGuid(),writeOut);
		
		if (prereq instanceof AttributePrereq) {
			AttributePrereq prereqAttribute = (AttributePrereq) prereq;			
			writeOut.writeInt(PREREQ_Attribute);			
			
			String attributeName = prereqAttribute.getAttributeName();
			writeString(attributeName, writeOut);			
			writeOut.writeInt(prereqAttribute.getValue());
			return;
		}
		if (prereq instanceof SfPrereq) {
			SfPrereq prereqSf = (SfPrereq) prereq;			
			writeOut.writeInt(PREREQ_SF);
			
			String name = prereqSf.getSfName(); 
			writeString(name, writeOut);
			return;
		}	
	}
	
	static Prereq readPrereq(DataInputStream input) throws IOException{
		//read baseData
		String guid = readString(input);
		int prereqType = input.readInt();
		switch (prereqType) {
			case PREREQ_Attribute:{
				String attributeName = readString(input);
				int value = input.readInt();
				AttributePrereq prereq = new AttributePrereq(attributeName, value);
				setPrereqBaseData(prereq, guid);
				return prereq;
			}
			case PREREQ_SF:{
				String sfName = readString(input);				
				SfPrereq prereq = new SfPrereq(sfName);
				setPrereqBaseData(prereq, guid);
				return prereq;
			}
			

		
		}
		return null;
	}	
	private static void setPrereqBaseData(Prereq prereq,String guid){
		prereq.setGuid(guid);
	}
	
	
	static void writeSF(SF sf,DataOutputStream writeOut) throws IOException{
		writeString(sf.getGuid(), writeOut);
		
		writeString(sf.getName(), writeOut);
		
		writeOut.writeInt(sf.getSFType());
	}
	
	static SF readSF(DataInputStream input) throws IOException{
		String guid = readString(input);
		
		SF sf = new SF();
		String sfName = readString(input);
		sf.setName(sfName);
		int sfType = input.readInt();
		sf.setSFType(sfType);
		
		setSFBaseData(sf, guid);
		return null;
	}
	
	private static void setSFBaseData(SF sf,String guid){
		sf.setGuid(guid);
	}
	
	static void writeManeuverDesc(ManeuverDesc maneuverDesc,DataOutputStream writeOut) throws IOException{
		String guid = maneuverDesc.getGuid();
		writeString(guid,writeOut);
		
		String maneuverName = maneuverDesc.getManeuverName();
		writeString(maneuverName, writeOut);
		
		writeOut.writeInt(maneuverDesc.getModifier());
				
		int countOfPrereq = maneuverDesc.getPrereqs().size();
		writeOut.writeInt(countOfPrereq);
		for(int i = 0;i<countOfPrereq;i++){
			Prereq prereq = maneuverDesc.getPrereqs().get(i);
			writePrereq(prereq, writeOut);
		}
	}
	
	static ManeuverDesc readManeuverDesc(DataInputStream input) throws IOException{
		String guid = readString(input);
		
		String maneuverName = readString(input);
		int modifier 	= input.readInt();
		
		ManeuverDesc maneuverDesc = new ManeuverDesc(maneuverName);
		maneuverDesc.setModifier(modifier);
		
		int countOfPrereq = input.readInt();
		for(int i = 0;i< countOfPrereq;i++){
			Prereq prereq = readPrereq(input);
			maneuverDesc.addPrerequisite(prereq);
		}
		setManeuverDescBaseData(maneuverDesc, guid);
		return maneuverDesc;
	}
	private static void setManeuverDescBaseData(ManeuverDesc maneuverDesc,String guid){
		maneuverDesc.setGuid(guid);
	}
	
//	private static final int WeaponValueMod_Base	= 1<<1;
	private static final int WEAPONVALUEMOD_Ini		= 1<<2;
	private static final int WEAPONVALUEMOD_TPKK	= 1<<3;
	private static final int WEAPONVALUEMOD_TP		= 1<<4;
	private static final int WEAPONVALUEMOD_WM		= 1<<5;
	
	static void writeWeaponValueMod(WeaponValueModifier valueMod,DataOutputStream writeOut) throws IOException{
		//write basedata
		writeString(valueMod.getGuid(),writeOut);		
		writeOut.writeInt(valueMod.getType());
		
		if (valueMod instanceof WeaponINIModifier) {
			WeaponINIModifier iniModifier = (WeaponINIModifier) valueMod;			
			writeOut.writeInt(WEAPONVALUEMOD_Ini);			
						
			writeOut.writeInt(iniModifier.getIniMod());
			return;
		}
		if (valueMod instanceof WeaponTPKKModifier) {
			WeaponTPKKModifier tpkkModifier = (WeaponTPKKModifier) valueMod;			
			writeOut.writeInt(WEAPONVALUEMOD_TPKK);			
						
			writeOut.writeInt(tpkkModifier.getKkMod());
			writeOut.writeInt(tpkkModifier.getStepMod());
			return;
		}
		if (valueMod instanceof WeaponTPModifier) {
			WeaponTPModifier iniModifier = (WeaponTPModifier) valueMod;			
			writeOut.writeInt(WEAPONVALUEMOD_TP);			
						
			writeOut.writeInt(iniModifier.getTpBonusMod());
			writeOut.writeInt(iniModifier.getTpDiceNumberMod());
			writeOut.writeInt(iniModifier.getTpDiceSidesMod());
			return;
		}
		if (valueMod instanceof WeaponWMModifier) {
			WeaponWMModifier wmModifier = (WeaponWMModifier) valueMod;			
			writeOut.writeInt(WEAPONVALUEMOD_WM);			
						
			writeOut.writeInt(wmModifier.getAtMod());
			writeOut.writeInt(wmModifier.getPaMod());			
			return;
		}
	}
	
	static WeaponValueModifier readWeaponValueMod(DataInputStream input) throws IOException{
		//read baseData
		String guid = readString(input);
		int type =	input.readInt();
		
		int weaponValueModType = input.readInt();
		switch (weaponValueModType) {
			case WEAPONVALUEMOD_Ini:{
				int iniMod =input.readInt() ;
				
				WeaponINIModifier valueModifier = new WeaponINIModifier(type, iniMod);
				
				setWeaponValueModBaseData(valueModifier, guid, type);
				return valueModifier;
			}
			case WEAPONVALUEMOD_TPKK:{
				int kkMod =input.readInt() ;
				int stepMod =input.readInt() ;
				
				WeaponTPKKModifier valueModifier = new WeaponTPKKModifier(type, kkMod,stepMod);
				
				setWeaponValueModBaseData(valueModifier, guid, type);
				return valueModifier;
			}
			case WEAPONVALUEMOD_TP:{
				int tpBonusMod =input.readInt() ;
				int tpDiceNumberMod =input.readInt() ;
				int tpDiceSidesMod =input.readInt() ;
				
				WeaponTPModifier valueModifier = new WeaponTPModifier(type, tpDiceNumberMod,tpDiceSidesMod,tpBonusMod);
				
				setWeaponValueModBaseData(valueModifier, guid, type);
				return valueModifier;
			}
			case WEAPONVALUEMOD_WM:{
				int atMod =input.readInt() ;
				int paMod =input.readInt() ;
				
				WeaponWMModifier valueModifier = new WeaponWMModifier(type, atMod,paMod);
				
				setWeaponValueModBaseData(valueModifier, guid, type);
				return valueModifier;
			}
		}
		return null;
	}	
	private static void setWeaponValueModBaseData(WeaponValueModifier weaponValueMod,String guid,int type){
		weaponValueMod.setGuid(guid);
		weaponValueMod.setType(type);		
	}
	
	static void writeWeaponStyle(WeaponStyle weaponStyle,DataOutputStream writeOut) throws IOException{
		String guid = weaponStyle.getGuid();
		writeString(guid,writeOut);
		
		String name = weaponStyle.getName();
		writeString(name, writeOut);
		
		String talentName = weaponStyle.getTalentName();
		writeString(talentName, writeOut);
				
		int countOfPrereq = weaponStyle.getPrerequisites().size();
		writeOut.writeInt(countOfPrereq);
		for(int i = 0;i<countOfPrereq;i++){
			Prereq prereq = weaponStyle.getPrerequisites().get(i);
			writePrereq(prereq, writeOut);
		}
		int countOfWeaponValueMods = weaponStyle.getWeaponValueModifiers().size();
		writeOut.writeInt(countOfWeaponValueMods);
		for(int i = 0;i<countOfWeaponValueMods;i++){
			WeaponValueModifier weaponValueMod = weaponStyle.getWeaponValueModifiers().get(i);
			writeWeaponValueMod(weaponValueMod, writeOut);
		}
		int countOfManeuvers = weaponStyle.getPossibleManeuvers().size();
		writeOut.writeInt(countOfManeuvers);
		for(int i = 0;i<countOfManeuvers;i++){
			ManeuverDesc maneuver = weaponStyle.getPossibleManeuvers().get(i);
			writeManeuverDesc(maneuver, writeOut);
		}
	}
	
	static WeaponStyle readWeaponStyle(DataInputStream input) throws IOException{
		String guid = readString(input);
		
		String name = readString(input);
		
		String talentName = readString(input);
		
		WeaponStyle style = new WeaponStyle(talentName);
			
		int countOfPrereq = input.readInt();
		for(int i = 0;i< countOfPrereq;i++){
			Prereq prereq = readPrereq(input);
			style.addPrerequisite(prereq);
		}
		int countOfWeaponvalueMods = input.readInt();
		for(int i = 0;i< countOfWeaponvalueMods;i++){
			WeaponValueModifier weaponValueMod = readWeaponValueMod(input);
			style.addWeaponValueModifer(weaponValueMod);
		}
		int countOfManeuvers = input.readInt();
		for(int i = 0;i< countOfManeuvers;i++){
			ManeuverDesc maneuver = readManeuverDesc(input);
			style.addManeuver(maneuver);
		}
		setWeaponStyleBaseData(style, guid,name,talentName);
		return style;
	}
	private static void setWeaponStyleBaseData(WeaponStyle weaponStyle,String guid,String name,String talentName){
		weaponStyle.setGuid(guid);
		weaponStyle.setName(name);
		weaponStyle.setTalentName(talentName);
	}

}
